#include <map>
#include <iostream>
#include <sstream>
#include "spdlog/spdlog.h"
#include "spdlog/common.h"
#include "spdlog/async.h"
#include "spdlog/async_logger.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "Ini/Ini.h"
#include "Directory/Directory.h"
#include "DateTime/DateTime.h"
#include "File/File.h"
#include "Logger.h"

using namespace SystemTool;

// Construct logger
Logger* Logger::GetInstance()
{
    static Logger LogInstance;
    return &LogInstance;
}

// Construct the obj
Logger::Logger()
{
    m_pbIsInited = new std::atomic<bool>;
}

// Destruct the obj
Logger::~Logger()
{
    if(m_pbIsInited)
    {
        delete m_pbIsInited;
        m_pbIsInited = nullptr;
    }
}

// Init log cfg
void Logger::InitCfg()
{
    if (!(m_pbIsInited->load()))
    {
        std::string strCfgPath = SystemTool::Directory::GetAppDataPath() + "/LogCfg.ini";

        int iRotationSize = 20;
        if (!SystemTool::Ini::GetKey(strCfgPath, "Log", "RotationSize", iRotationSize))
        {
            SystemTool::Ini::SetKey(strCfgPath, "Log", "RotationSize", iRotationSize);
        }
        m_iRotationSize = iRotationSize;

        int iCutoffCnt = 3;
        if (!SystemTool::Ini::GetKey(strCfgPath, "Log", "PurgeCnt", iCutoffCnt))
        {
            SystemTool::Ini::SetKey(strCfgPath, "Log", "PurgeCnt", iCutoffCnt);
        }
        m_iCutoffCnt = iCutoffCnt;

        bool bIsOpenDebugLog = false;
        if (!SystemTool::Ini::GetKey(strCfgPath, "Log", "IsEnableDebugLog", bIsOpenDebugLog))
        {
            SystemTool::Ini::SetKey(strCfgPath, "Log", "IsEnableDebugLog", bIsOpenDebugLog);
        }
        m_bIsOpenDebugLog = bIsOpenDebugLog;
        m_pbIsInited->store(true);
    }
}

// Add the logger
void Logger::AddFileLogger(std::string strModuleName, std::string strLogFilePath)
{
    if(strLogFilePath.empty())
    {
        return;
    }

    InitCfg();

    try
    {
        std::string strPattern = "[%Y-%m-%d %H:%M:%S:%e][%P][%t][%L][%s][%#][%n][%v]";
        auto FileRotationSink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(strLogFilePath.c_str(), m_iRotationSize * 1024 * 1024, m_iCutoffCnt);
        FileRotationSink->set_pattern(strPattern);
        if (m_bIsOpenDebugLog)
        {
            FileRotationSink->set_level(spdlog::level::trace);
        }
        else
        {
            FileRotationSink->set_level(spdlog::level::info);
        }

        std::vector<spdlog::sink_ptr> sinks{FileRotationSink};
        auto FileLogger = std::make_shared<spdlog::logger>(strModuleName, sinks.begin(), sinks.end());
        if (m_bIsOpenDebugLog)
        {
            FileLogger->set_level(spdlog::level::trace);
        }
        else
        {
            FileLogger->set_level(spdlog::level::info);
        }
        spdlog::register_logger(FileLogger);
    }
    catch (const spdlog::spdlog_ex &ex) 
    {
        std::cout << "Err happened:" << ex.what() << std::endl;
    }
}

// Add the logger
void Logger::AddConsoleLogger(std::string strModuleName)
{
    try
    {
        std::string strPattern = "[%Y-%m-%d %H:%M:%S:%e][%P][%t][%L][%s][%#][%n][%v]";
        auto ConsoleSink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
        ConsoleSink->set_level(spdlog::level::debug);
        ConsoleSink->set_pattern(strPattern);

        std::vector<spdlog::sink_ptr> sinks{ConsoleSink};
        auto ConsoleLogger = std::make_shared<spdlog::logger>(strModuleName, sinks.begin(), sinks.end());
        if (m_bIsOpenDebugLog)
        {
            ConsoleLogger->set_level(spdlog::level::trace);
        }
        else
        {
            ConsoleLogger->set_level(spdlog::level::info);
        }
        spdlog::register_logger(ConsoleLogger);
    }
    catch (const spdlog::spdlog_ex &ex) 
    {
        std::cout << "Err happened:" << ex.what() << std::endl;
    }
}

// Get logger
std::shared_ptr<spdlog::logger> Logger::GetLogger(std::string strModuleName)
{
    return spdlog::get(strModuleName);
}

// Log the msg
void Logger::Log(std::string strModuleName, int iLevel, std::string strFilePath, int iLineNo, std::string strMsg)
{
    try
    {
        std::string strFileName = SystemTool::File::GetFileName(strFilePath);
        // Log msg to file
        auto CurLogger = spdlog::get(strModuleName);

        if (iLevel == 4/*Error*/)
        {
            CurLogger->error(strModuleName,strFilePath, iLineNo, strMsg);
        }
        else if (iLevel == 3 /*Warn*/)
        {
            // CurLogger->warn("[{}][{}][{}]", strFilePath, iLineNo, strMsg);
            CurLogger->warn(strModuleName,strFilePath, iLineNo, strMsg);
        }
        else if (iLevel == 2 /*info*/)
        {
            // CurLogger->info("[{}][{}][{}]", strFilePath, iLineNo, strMsg);
            CurLogger->info(strModuleName,strFilePath, iLineNo, strMsg);
        }
        else if (iLevel == 1 /*debug*/)
        {
            // CurLogger->debug("[{}][{}][{}]", strFilePath, iLineNo, strMsg);
            CurLogger->debug(strModuleName,strFilePath, iLineNo, strMsg);
        }
    }
    catch (const spdlog::spdlog_ex &ex) 
    {
        std::cout << "Err happened:" << ex.what() << std::endl;
    }
}
